import sys
from time import sleep
import pygame
from settings import Settings
from game_stats import Gamestats
from ship import Ship
from beijing import Beijing
from bullet import Bullet
from alien import Alien
from button import Button
from scoreboard import Scoreboard


class AlienInvasion:
    """管理游戏资源和行为的类"""

    def __init__(self):
        """初始化游戏并创建游戏资源"""
        pygame.init()
        pygame.mixer.init()
        self.settings = Settings()  # 将类里的函数赋给一个变量，这部一定要有
        pygame.time.set_timer(Settings.FIRE_EVENT, 125)
        self.screen = pygame.display.set_mode((self.settings.screen_width, self.settings.screen_height))  # 设置屏幕大小
        """全屏显示的代码，需要可开启"""
        # self.screen = pygame.display.set_mode((0, 0), pygame.FULLSCREEN)
        # self.settings.screen_width = self.screen.get_rect().width
        # self.settings.screen_height = self.screen.get_rect().height

        pygame.display.set_caption("Alien Invasion")  # 设置标题

        self.stats = Gamestats(self)
        self.ship = Ship(self)
        self.beijing = Beijing(self)
        self.sb = Scoreboard(self)
        self.bullets = pygame.sprite.Group()
        self.aliens = pygame.sprite.Group()

        self._create_fleet()
        self.paly_button = Button(self, "play game")

    def run_game(self):
        """开始游戏的主循环"""
        while True:
            self._check_events()
            if self.stats.game_active:
                self.ship.move()
                self._update_bullets()
                self._update_aliens()
            self._update_screen()

    def _check_events(self):
        # 监视键盘和鼠标事件。
        for event in pygame.event.get():
            key_press = pygame.key.get_pressed()
            if key_press[pygame.K_SPACE]:
                """创建一颗子弹，并将其加入编组bullets中"""
                if len(self.bullets) < self.settings.bullets_allowed and event.type == (
                        Settings.FIRE_EVENT):
                    # 播放开枪声音
                    fire = pygame.mixer.Sound('music/gun.mp3')
                    pygame.mixer.Sound.play(fire)
                    # 创造一颗子弹
                    new_bullet = Bullet(self)  # 引用Bullet类创建子弹赋值给new_bullet
                    self.bullets.add(new_bullet)  # 在sprite中添加子弹
            if event.type == pygame.QUIT:
                self.write_high_score()
                sys.exit()
            # 移动
            elif event.type == pygame.KEYDOWN:  # 按下键触发
                self.keydown_events(event)
            elif event.type == pygame.KEYUP:  # 松开键触发
                self.keyup_events(event)
            elif event.type == pygame.MOUSEBUTTONDOWN:
                mouse_pos = pygame.mouse.get_pos()
                self._check_paly_button(mouse_pos)

    def keydown_events(self, event):
        if event.key == pygame.K_RIGHT:
            self.ship.moving_right = True
        elif event.key == pygame.K_LEFT:
            self.ship.moving_left = True
        elif event.key == pygame.K_UP:
            self.ship.moving_up = True
        elif event.key == pygame.K_DOWN:
            self.ship.moving_down = True
        elif event.key == pygame.K_q:
            self.write_high_score()
            sys.exit()
        # elif event.key == pygame.K_SPACE:
        #     self._fire_bullet()

    def keyup_events(self, event):
        if event.key == pygame.K_RIGHT:
            self.ship.moving_right = False
        elif event.key == pygame.K_LEFT:
            self.ship.moving_left = False
        elif event.key == pygame.K_UP:
            self.ship.moving_up = False
        elif event.key == pygame.K_DOWN:
            self.ship.moving_down = False

    def write_high_score(self):
        """写入最高得分"""
        with open('high_score.txt', 'w') as xie_ru:
            xie_ru.write(str(self.stats.high_score))

    def _check_paly_button(self, mouse_pos):
        """在玩家点击开始按钮时启动游戏"""
        if self.paly_button.rect.collidepoint(mouse_pos):
            # 重置由游戏设置并开始
            self.settings.initialize_dynamic_settings()
            self.stats.reset_stats()
            self.write_high_score()
            self.stats.game_active = True
            self.sb.prep_score()
            self.sb.prep_level()
            self.sb.prep_ships()
            # 屏幕内不显示鼠标
            pygame.mouse.set_visible(False)

    def _create_fleet(self):
        """创建外星人群"""
        # 创建一个外星人并计算一行可容纳多少外星人
        # 外星人的间隔为外星人的宽度
        alien = Alien(self)  # 引用Alien类创建外星人赋值给alien 主要用rect函数
        alien_width, alien_height = alien.rect.size  # size获取宽和高
        # 计算一行可以容纳几个外星人
        available_space_x = self.settings.screen_width - (2 * alien_width)
        numbers_alien_x = round(available_space_x / (2 * alien_width))
        # 计算屏幕可以容纳几行外星人
        available_space_y = self.settings.screen_height - (15 * alien_height)
        numbers_alien_y = available_space_y // (2 * alien_height)
        # 创建第一行外星人
        for alien_rows in range(numbers_alien_y):
            for alien_number in range(numbers_alien_x):
                self._create_alien(alien_number, alien_rows)

    def _create_alien(self, alien_number, alien_rows):
        # 创建一个外星人并加入当前行
        alien = Alien(self)  # 引用Alien类创建外星人赋值给alien 一个一个创建外星人
        alien_width, alien_height = alien.rect.size
        # 每个number向右移两个长度
        alien.x = alien_width + 2 * alien_width * alien_number
        alien.rect.x = alien.x
        alien.rect.y = alien.rect.height + 3.5 * alien.rect.height * alien_rows
        self.aliens.add(alien)  # 在sprite中添加外星人

    def _check_fleet_edges(self):
        """有外星人到达边缘时采取相应的措施"""
        for alien in self.aliens.sprites():
            if alien.check_edges():
                self._change_fleet_direction()
                break

    def _change_fleet_direction(self):
        """将外星人舰队下移，并改变他们的方向"""
        for alien in self.aliens.sprites():
            alien.rect.y += self.settings.fleet_drop_speed
        self.settings.fleet_direction *= -1

    def _update_bullets(self):
        """更新子弹的位置并删除子弹"""
        # 更新子弹的位置
        self.bullets.update()
        # 删除小时的子弹
        for bullet in self.bullets.copy():
            if bullet.rect.bottom <= 0:
                self.bullets.remove(bullet)

        """检查是否有子弹击中了目标，如果是就删除子弹和目标"""
        # 放在这是因为while循环最后执行这一步
        collisions = pygame.sprite.groupcollide(self.bullets, self.aliens, True, True)
        if collisions:
            # 播放爆炸音效
            boom = pygame.mixer.Sound('music/boom.mp3')
            pygame.mixer.Sound.play(boom)
            # 击杀得分和更新最高分
            for aliens in collisions.values():
                self.stats.score += self.settings.alien_points * len(aliens)
                self.sb.prep_score()
                self.sb.check_high_score()
        if not self.aliens:
            # 删除现有的子弹并新建外星人舰队
            self.bullets.empty()
            self._create_fleet()
            self.settings.increase_speed()
            # 提高等级
            self.stats.level += 1
            self.sb.prep_level()

    def _update_aliens(self):
        """检查外星人是否在屏幕边缘，然后更新外星人群中所有外星人的位置"""
        self._check_fleet_edges()
        self.aliens.update()
        # 检测飞船是否与外星人发生了碰撞
        if pygame.sprite.spritecollideany(self.ship, self.aliens):
            self._ship_hit()
        # 检测飞船是否到达了底部
        self._check_aliens_bottom()

    def _ship_hit(self):
        """响应飞船被撞到"""
        # 飞船数减一
        self.stats.ship_left -= 1
        self.sb.prep_ships()
        # 清空所有元素
        self.aliens.empty()
        self.bullets.empty()
        # 创建和重置元素
        self._create_fleet()
        self.ship.center_ship()
        # 暂停
        sleep(0.5)
        if self.stats.ship_left == 0:
            self.stats.game_active = False
            pygame.mouse.set_visible(True)

    def _check_aliens_bottom(self):
        """检查外星人是否到达屏幕底部"""
        screen_rect = self.screen.get_rect()
        for alien in self.aliens.sprites():
            if alien.rect.bottom >= screen_rect.bottom:
                # 像飞创被撞一样处理
                self._ship_hit()
                break

    def _update_screen(self):
        # 每次循环时都填满颜色
        self.screen.fill(self.settings.bg_color)
        # 导入图片并放在想要的位置
        self.beijing.blitme()
        self.ship.blitm()
        # 把子弹和外星人画在屏幕上
        for bullet in self.bullets.sprites():
            bullet.draw_bullet()  # 由于是系统再带矩形，需要设置rect，color等，引用bullet类的函数
        self.aliens.draw(self.screen)
        # 显示得分
        self.sb.show_score()
        # 如果游戏处于非活跃状态，就绘制paly按钮
        if not self.stats.game_active:
            self.paly_button.draw_button()
        # 让最近绘制的屏幕可见。
        pygame.display.flip()  # 这个循环主要循行的是这个代码,为了不断刷新屏幕


if __name__ == '__main__':
    # 创建游戏实例并运行游戏。
    ai = AlienInvasion()
    ai.run_game()
